package ru.ifmo.thesis.suite;

import java.io.FileInputStream;
import java.io.IOException;

import org.jgap.event.GeneticEvent;
import org.jgap.event.GeneticEventListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ru.ifmo.thesis.domain.Shedule;
import ru.ifmo.thesis.event.Event;
import ru.ifmo.thesis.event.EventListener;
import ru.ifmo.thesis.event.EventManager;
import ru.ifmo.thesis.gp.MyConfigurationOptionsBuilder;
import ru.ifmo.thesis.solver.gp.GPSolver;
import ru.ifmo.thesis.solver.gp.NoChangesRule;
import ru.ifmo.thesis.task.Task;
import ru.ifmo.thesis.util.Algorithms;
import ru.ifmo.thesis.util.Loggers;

public class SuiteRunner {

	private static final Logger logger = LoggerFactory.getLogger(SuiteRunner.class);

	public static class TaskStartedEvent extends Event {

		public static final String NAME = "NEXT_TASK_STARTED_EVENT";
		
		public TaskStartedEvent(Object source) {
			super(source, NAME);
		}
	}

	public static class TaskFinishedEvent extends Event {

		public static final String NAME = "TASK_FINISHED_EVENT";
		private final Shedule shedule;
		
		public TaskFinishedEvent(Object source, Shedule shedule) {
			super(source, NAME);
			this.shedule = shedule;
		}
		
		public Shedule getShedule() {
			return shedule;
		}
	}
	
	public static class NextGenerationEvolved extends Event {

		public static final String NAME = "NEXT_GENERATION_EVOLVED";
	
		private final double sheduleCost;
		
		public NextGenerationEvolved(Object source, double sheduleCost) {
			super(source, NAME);
			this.sheduleCost = sheduleCost;
		}
		
		public double getSheduleCost() {
			return sheduleCost;
		}
	}
	
	private final EventManager eventManager = new EventManager();
	
	public void run(String[] args) throws IOException {
        Loggers.initLoggerFromConfig();
		
		String suiteFileName = args[0];
		
		String outputFileName = args[1];
		
		TaskSuite taskSuite = SuiteSerializer.readZippedSuite(
				new FileInputStream(suiteFileName));
		
//		final GPSolver solver = new GPSolver(new FixedIterations(500), 30);
		final GPSolver solver = new GPSolver(new NoChangesRule(100000), new MyConfigurationOptionsBuilder().build());
//		final GPSolver solver = new GPSolver(new FixedTimeCalculationRule(new Minutes(30)), 30);
		
//		SuiteRunnerLogger solverXSLXLogger = new SuiteRunnerXSLXLogger(outputFileName, solver, this);
		SuiteRunnerLogger suiteLogger = new SuiteRunnerTextFileLogger(outputFileName, this);
		
		
		for (Task t : taskSuite.getTasks()) {
			eventManager.fireEvent(new TaskStartedEvent(this));
			
			System.err.println("next task");
			
			solver.getConfiguration().getEventManager().addEventListener(
					GeneticEvent.GENOTYPE_EVOLVED_EVENT, new GeneticEventListener() {
						
						@Override
						public void geneticEventFired(GeneticEvent event) {
							Shedule bestSolution = solver.bestSolutionSoFar();
							double sheduleCost = Algorithms.calculateSheduleCost(bestSolution);
							logger.debug(String.format("Logged shedule=%d, cost=%f", bestSolution.hashCode(), sheduleCost));
							eventManager.fireEvent(new NextGenerationEvolved(SuiteRunner.this, sheduleCost));
						}
					});
			
			long start = System.nanoTime();
			solver.solve(t);
			System.err.println("time = " + (System.nanoTime() - start));
			solver.reset();
			eventManager.fireEvent(new TaskFinishedEvent(this, solver.bestSolutionSoFar()));
		}
		
		suiteLogger.close();
	}
	
	public static void main(String[] args) throws Exception {
		new SuiteRunner().run(args);
	}
	
	public void addEventListener(String eventName, EventListener listener) {
		this.eventManager.addEventListener(eventName, listener);
	}

	public void removeEventListener(String name,
			EventListener listener) {
		eventManager.removeEventListener(name, listener);
	}
}
